package com.xiaoxian.basic.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.xiaoxian.basic.commoneumn.ErrorCode;
import com.xiaoxian.basic.constant.Constant;
import com.xiaoxian.basic.domain.dto.BinderDto;
import com.xiaoxian.basic.domain.dto.LoginDto;
import com.xiaoxian.basic.domain.dto.SmsCode;
import com.xiaoxian.basic.exception.CustomException;
import com.xiaoxian.basic.service.ILoginService;
import com.xiaoxian.basic.util.AjaxResult;
import com.xiaoxian.basic.util.HttpClientUtils;
import com.xiaoxian.basic.util.MD5Utils;
import com.xiaoxian.basic.util.StrUtils;
import com.xiaoxian.basic.util.jwt.JwtUtils;
import com.xiaoxian.basic.util.jwt.RsaUtils;
import com.xiaoxian.basic.util.jwt.UserInfo;
import com.xiaoxian.system.domain.Menu;
import com.xiaoxian.system.mapper.MenuMapper;
import com.xiaoxian.system.mapper.PermissionMapper;
import com.xiaoxian.user.domain.LoginInfo;
import com.xiaoxian.user.domain.User;
import com.xiaoxian.user.domain.Wxuser;
import com.xiaoxian.user.mapper.LogininfoMapper;
import com.xiaoxian.user.mapper.UserMapper;
import com.xiaoxian.user.mapper.WxuserMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import springfox.documentation.swagger2.mappers.EnumMapper;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Value("${jwt.rsa.pri}")// SPEL表达式读取配置文件中的值
    private String jwtRsaPrivate;
    /**
     * 用户登录
     * @param dto
     * @return
     */
    @Override
    public AjaxResult usernameLogin(LoginDto dto) {
        //查询用户是否存在（用户名，电话，email）
        LoginInfo loginInfo= logininfoMapper.findByUsername(dto.getUsername(),dto.getType());
        //判断是否存在
        if(loginInfo==null){
            throw new CustomException(ErrorCode.USERNAME_NOT_EXIST.getCode());
        }
        //判断密码是否正确
//        //获取盐值
//        String salt = loginInfo.getSalt();
//        //盐值拼接密码加密
//        String pwd = MD5Utils.encrypByMd5(salt + dto.getPassword());
//        //判断密码是否一致
//        if(!pwd.equals(loginInfo.getPassword())){
//            throw new CustomException(ErrorCode.PASSWORD_NOT_DEF.getCode());
//        }
        //HashMap<String, Object> map = getHashMap(loginInfo);
        return AjaxResult.my().setPath(loginSuccessJwtHandler(loginInfo));
    }


    /**
     * wechat登录
     * @param map
     * @return
     */
    @Override
    public AjaxResult wechatLogin(Map<String, String> map) {

        //查询code是否存在
        String code = map.get("code");
        if(code==null){
            throw new CustomException(ErrorCode.CODE_NOT_EXIST.getCode());
        }
        //code存在需要去通过code获取access_token和openid,先定义常量
        String url = Constant.wechat.GET_TOKEN
                .replace("APPID",Constant.wechat.GET_APPID)
                .replace("SECRET",Constant.wechat.GET_SECRET)
                .replace("CODE",code);
        //发送请求
        String s = HttpClientUtils.httpGet(url);
        //将获取的json字符串转换成json对象
        JSONObject jsonObject = JSONObject.parseObject(s);
        //取出access_token和openid
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        //首先查询openid是否存在
        Wxuser wxuser=wxuserMapper.findByOpenid(openid);
        if (wxuser==null){
            //没有就需要去binder页面注册，并且携带access_token和openid
            //先拼接字符串
            String str="?access_token="+access_token+"&openid="+openid;
            //返回给前端
            return AjaxResult.my().setPath(str).setMsg("noWxuser");
        }
        //通过查询到的wxuser的user_id去查询user在通过user中的logininfoid去logininfo中查询数据
        LoginInfo loginInfo=userMapper.findLogininfoByuserId(wxuser.getUserId());
        //调用封装返回的数据

        return AjaxResult.my().setPath(loginSuccessJwtHandler(loginInfo));
    }

    /**
     * 绑定登录
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public AjaxResult binder(BinderDto dto) {
        //判断两次密码一致
        if(!dto.getPassword().equals(dto.getConfirmpassword())){
            throw new CustomException(ErrorCode.SHOP_PASSWORD_DEF.getCode());
        }
        //查询用户是否注册
        User byPhone = userMapper.findByPhone(dto.getUsername());
        if(byPhone!=null){
            throw new CustomException(ErrorCode.PHONE_EXIST.getCode());
        }
        //发送请求获取信息
        //第一常量构建请求地址
        String user_url = Constant.wechat.GET_USER_INFO
                .replace("ACCESS_TOKEN", dto.getAccessToken())
                .replace("OPENID", dto.getOpenId());
        //请求获取信息
        String getUserInfo = HttpClientUtils.httpGet(user_url);
        //保存信息 logininfo user wxuser

        //构建logininfo
        //生产盐值随机字符串
        String salt = StrUtils.getComplexRandomString(64);
        //拼接盐值MD5加密
        String pwdMD5 = MD5Utils.encrypByMd5(salt + dto.getPassword());
        //调用获取logininfo对象
        LoginInfo loginInfo = setLoginInfo(dto, salt, pwdMD5);
        logininfoMapper.add(loginInfo);
        //构建user对象
        User user = setUser(loginInfo);
        userMapper.add(user);
        //构建wxuser
        Wxuser wxuser = JSONObject.parseObject(getUserInfo, Wxuser.class);
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);
        //调用封装返回的数据

        return AjaxResult.my().setPath(loginSuccessJwtHandler(loginInfo));
    }



    private LoginInfo setLoginInfo(BinderDto dto, String salt, String pwdMD5) {
        //创建logininfo对象
        LoginInfo loginInfo = new LoginInfo();
        //设置密码
        loginInfo.setPassword(pwdMD5);
        //设置盐值
        loginInfo.setSalt(salt);
        //设置用户名
        loginInfo.setUsername(dto.getUsername());
        //设置电话
        loginInfo.setPhone(dto.getUsername());
        //设置身份
        loginInfo.setType(1);
        //设置状态
        loginInfo.setState(1);
        return loginInfo;
    }

    private User setUser(LoginInfo loginInfo) {
        //创建User对象
        User user = new User();
        //将logininfo复制给user
        BeanUtils.copyProperties(loginInfo,user);
        user.setCreatetime(new Date());
        user.setLogininfoId(loginInfo.getId());
        return user;
    }
    /**
     * 登录成功处理
     * @param logininfo
     * @return
     */
    private Map<String, Object> loginSuccessJwtHandler(LoginInfo logininfo) {
        //创建LoginData对象用于等会通过私钥加密
        UserInfo userInfo = new UserInfo();
        userInfo.setLoginInfo(logininfo);
        Map<String, Object> map = new HashMap<>();
        //判断当前用户是主站还是后端管理系统
        if(logininfo.getType() == 1){
            //单纯返回个人信息
            try {
                PrivateKey privateKey = RsaUtils
                        .getPrivateKey(FileCopyUtils
                                .copyToByteArray(this.getClass().getClassLoader().getResourceAsStream(jwtRsaPrivate)));
                //加密得到token
                String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 60*24);
                logininfo.setPassword(null);
                logininfo.setSalt(null);
                map.put("loginInfo",logininfo);
                map.put("token",token);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            } catch (InvalidKeySpecException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }else{
            //如果是后端，返回个人信息+权限信息
            List<String> sns = permissionMapper.loadPermissionByLoginInfoId(logininfo.getId());
            List<Menu> menus = menuMapper.loadMenuByLoginInfoId(logininfo.getId());
            userInfo.setPermissions(sns);
            userInfo.setMenus(menus);
            try {
                //获取私钥
                PrivateKey privateKey = RsaUtils
                        .getPrivateKey(FileCopyUtils
                                .copyToByteArray(this.getClass().getClassLoader().getResourceAsStream(jwtRsaPrivate)));
                //加密得到token
                String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 60*24);
                logininfo.setPassword(null);
                logininfo.setSalt(null);
                map.put("loginInfo",logininfo);
                map.put("token",token);
                map.put("permissions",sns);
                map.put("menus",menus);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            } catch (InvalidKeySpecException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return map;
    }
}
